/**
  * ABaseAction.java
 *
 * Copyright (C) 2009,  Tomislav Milkovic
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */
package ws4is.engine.actions;


import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.util.Map;

import javax.servlet.FilterChain;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import ws4is.engine.ConfigObject;
import ws4is.engine.enums.HTTPRequestType;

import com.google.gson.Gson;
import com.google.gson.JsonObject;

/*
 * Base Action class implementing core engine
 */
abstract class ABaseAction implements IAction{

	private transient static String SETTER ="set";	
	private transient String param_cmd = null;
	private transient PrintWriter pw = null;
	protected transient Gson gson = new Gson();		
	
	
    protected ABaseAction() {
		super();
	}
	
    //process http request http://..../cmd=[methodName]
    //where [methodName] is public method of the class
    //Exmaple : http://localhost:8080/myapp/cmd=testConenction 
    //        will call testConnection public method
	
    //process http request http://..../cmd=[methodName]
    //where [methodName] is public method of the class
    //Exmaple : http://localhost:8080/myapp/cmd=testConenction 
    //        will call testConnection public method
	

	public final void execute(final ConfigObject config, final HTTPRequestType type , final HttpServletRequest request, final HttpServletResponse response, final FilterChain chain) throws Exception, IOException {
		param_cmd = preExecute(type, request);
		String cmd = null;
		
		switch (type){
			case GET : {
						if (param_cmd==null) param_cmd = config.getCmdGet();
						cmd = request.getParameter(param_cmd);						
						break;
						}
			case FILTER : ;break;

			case POST : {
							if (param_cmd==null) param_cmd = config.getCmdPost();
				    		Map pmap = request.getParameterMap();
				    		if (pmap.containsKey(param_cmd)){
				    			cmd = request.getParameter(param_cmd);
				    		}  				    		
				    	}; break;			
			case MULTIPART : {
								Cookie[] cookies = request.getCookies();
								for (Cookie cookie : cookies){
									if(param_cmd.equals(cookie.getName())){
										cmd =  cookie.getValue();
										break;
									}
								} 
							};break;
			default : ;
		}
						
		cmd = config.getRemap(cmd);

		callMethod(cmd,config,type,request,response, chain);
	}    
    
    //optimization - cached methods for class 
	protected final Method getMethod(final ConfigObject config, final String methodName) throws Exception{
		Method method = config.getMethod(methodName);
		if (method!=null) return method;
    	Class[] args = {HttpServletRequest.class, HttpServletResponse.class};
    	method = getClass().getMethod(methodName, args);
    	config.setMethod(methodName, method);
    	return method;
    }    


	protected final void setSetters(final HTTPRequestType type , final HttpServletRequest request,final  HttpServletResponse response) throws Exception{
   	
    	Method [] methods =  this.getClass().getMethods();
    	for (Method method : methods){
    		String methodName = method.getName();
    		if (!methodName.startsWith(SETTER)) continue;
    		
    		methodName = methodName.substring(SETTER.length());
    		methodName = methodName.substring(0,1).toLowerCase() + methodName.substring(1);
    		
    		String value = null;
    		
    		switch (type){
				case GET : {value = request.getParameter(methodName);break;}
				case POST : {
			        		Map pmap = request.getParameterMap();
			        		if (pmap.containsKey(methodName)){
			        			value = request.getParameter(methodName);
			        		}  	
					    	}; break;			
				default : ;
    		}
    		
    		
			if (value==null) continue ; 
			

				Class<?>[] part = method.getParameterTypes();
				if(part.length>1)  continue;
				
				if (int.class.equals(part[0])){
					int i = Integer.parseInt(value);
					method.invoke(this, i);
				} else if (long.class.equals(part[0])){
					long i = Long.parseLong(value);
					method.invoke(this, i);
				} else if (float.class.equals(part[0])){
					float i = Float.parseFloat(value);
					method.invoke(this, i);					
				} else if (double.class.equals(part[0])){
					double d = Double.parseDouble(value);
					method.invoke(this, d);					
				} else if (boolean.class.equals(part[0])){
					boolean b = Boolean.parseBoolean(value);
					method.invoke(this, b);					
				}else method.invoke(this, value);		
    		
    	}
    }
    
    protected final void sendMessage(final HttpServletResponse response, boolean status , final String msg) throws Exception{
    	setJsonResponseHeader(response);
    	PrintWriter pw = getPrintWriter(response);
    	pw.print(getJSONResponse(status, msg));
    	closePrintWriter();
    }

	protected final void setJsonResponseHeader(final HttpServletResponse response){
		// Set to expire far in the past.
		response.setDateHeader("Expires", 0);
		// Set standard HTTP/1.1 no-cache headers.
		response.setHeader("Cache-Control",	"no-store, no-cache, must-revalidate");
		// Set IE extended HTTP/1.1 no-cache headers (use addHeader).
		response.addHeader("Cache-Control", "post-check=0, pre-check=0");
		// Set standard HTTP/1.0 no-cache header.
		response.setHeader("Pragma", "no-cache");
		// return a data type 
		response.setContentType("text/plain; charset=UTF-8");
	}
	

	protected final PrintWriter getPrintWriter(final HttpServletResponse response)throws IOException{
		if (pw!=null) return pw;
		pw = new PrintWriter(new OutputStreamWriter(response.getOutputStream(), "UTF8"), true);
		return pw;
	}
	
	//leaved protected, if user closes by force, new messages will not be sent to the browser
	//response stream will be closed
	protected final void closePrintWriter(){
		if (pw ==null) return;
		pw.flush();
		pw.close();
		pw = null;
	}
	
	/*
	 * Convert this object to JSON
	 */
	protected final String toJSON(){
		return gson.toJson(this);
	}
	
	protected final String getJSONResponse(boolean status, final String msg) {
		return getJSONResponse(status, msg, null);
	}
	
	protected final String getJSONResponse(boolean status, final String msg, final Object[][] params) {	
		JsonObject jo = new JsonObject();
		jo.addProperty("success", status);
		jo.addProperty("msg", msg);
		if(params!=null){
		 for(Object s[] : params){
			 if(Boolean.class.equals(s[1].getClass()))
				 jo.addProperty((String)s[0], (Boolean)s[1]);
			 if(Character.class.equals(s[1].getClass()))
				 jo.addProperty((String)s[0], (Character)s[1]);
			 if(Number.class.equals(s[1].getClass()))
				 jo.addProperty((String)s[0], (Number)s[1]);
			 if(String.class.equals(s[1].getClass()))
				 jo.addProperty((String)s[0], (String)s[1]);
		 }
		}		
		return  gson.toJson(jo);
	}
	
	 protected abstract  void callMethod(final String cmd, final ConfigObject config, final HTTPRequestType type , final HttpServletRequest request, final HttpServletResponse response , final FilterChain chain) throws Exception, IOException ;

}
